package com.bstek.bdf2.core.security.session;

import java.time.Duration;
import java.time.Instant;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArraySet;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationListener;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextImpl;
import org.springframework.security.core.session.SessionDestroyedEvent;
import org.springframework.security.core.session.SessionInformation;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.session.data.redis.ReactiveRedisSessionRepository;
import org.springframework.util.Assert;

import javax.annotation.Resource;

/**
 * @author Jacky.gao
 * @since 2013年12月19日
 */
public class SessionRegistryImpl implements SessionRegistry, ApplicationListener<SessionDestroyedEvent> {
    @Value("${spring.session.store-type}")
    private String type;
    @Resource
    private RedisTemplate redisTemplate;
    //~ Instance fields ================================================================================================

    protected final Logger logger = LoggerFactory.getLogger(SessionRegistryImpl.class);

    /** <principal:Object,SessionIdSet>
     *用户及其对应Session，一个用户可能有多个session
     * */
    private final ConcurrentMap<Object,Set<String>> principals =
            new ConcurrentHashMap<Object,Set<String>>();
    /** <sessionId:Object,SessionInformation>
     * SessionId及其对应的SessionInformation
     * */
    private final Map<String, SessionInformation> sessionIds =
            new ConcurrentHashMap<String, SessionInformation>();

    //~ Methods ========================================================================================================

    public List<Object> getAllPrincipals2() {
        Set<String> keys = redisTemplate.keys(ReactiveRedisSessionRepository.DEFAULT_NAMESPACE + ":sessions*");
        List<Object> users = new ArrayList<>(16);
        for (String key : keys) {
            if(key.indexOf("expires")==-1){
                Map<Object, Object> map = redisTemplate.opsForHash().entries(key);
                //获取sessionId
                String sessionId = key.replace(ReactiveRedisSessionRepository.DEFAULT_NAMESPACE + ":sessions:", "");
                long maxInactiveIntervalTime = ((Integer) map.get("maxInactiveInterval")).longValue();
                Duration maxInactiveInterval = Duration.ofSeconds(maxInactiveIntervalTime);
                //判断是否已经过期了这个session
                long lastAccessedTime = ((Long) map.get("lastAccessedTime")).longValue();
                if (Instant.now().minus(maxInactiveInterval).compareTo(Instant.ofEpochMilli(lastAccessedTime)) >= 0) {
                    continue;
                }
                HashMap<String, Object> info = new HashMap<>();
                Object o = map.get("sessionAttr:BDF_LOGIN_USER_");
                if(o!=null){
                    JSONObject jsonObject = JSONUtil.parseObj(o);
                    info.put("username", jsonObject.get("username"));
                    info.put("cname", jsonObject.get("cname"));
                    users.add(info);
                }

//                SecurityContextImpl securityContexts = (SecurityContextImpl) map.get("sessionAttr:SPRING_SECURITY_CONTEXT");
//                Authentication authentication = securityContexts.getAuthentication();
//                Object principal = authentication.getPrincipal();
//                JSONObject user = JSONUtil.parseObj(principal);
//                user.get("username");
//                user.get("cname");

            }

        }
        return users;
    }
   // 获取当前的所有主体信息
    @Override
    public List<Object> getAllPrincipals() {
        if(StringUtils.isNotEmpty(type)&&Objects.equals(type,"redis")){
            return getAllPrincipals2();
//            String currentCompany = ContextHolder.getLoginUser().getCompanyId();
//            List<Object> list = new ArrayList<>(16);
//            Set<String> keys = redisTemplate.keys("spring:session:sessions:*");
//            for (String key : keys) {
//                if(key.indexOf("expires")==-1){
//                    Object o = redisTemplate.opsForHash().get(key, "sessionAttr:SPRING_SECURITY_CONTEXT");
//                    if (o!=null){
//                        SecurityContextImpl securityContexts = (SecurityContextImpl)o;
//                         securityContexts.getAuthentication().getPrincipal();
//                         DefaultUser user = (DefaultUser) securityContexts.getAuthentication().getPrincipal();
//                        if (user.getCompanyId().equals(currentCompany)){
//                            Map<String,Object> map=new HashMap<String,Object>();
//                            map.put("username", user.getUsername());
//                            map.put("cname", user.getCname());
//                            list.add(map);
//                        }
//                    }
//                }
//            }
//            return list;
        }
        return new ArrayList<Object>(principals.keySet());
    }

    // 获取主体对应的会话信息，可包含过期或者不过期的SessionInformation
    @Override
    public List<SessionInformation> getAllSessions(Object principal, boolean includeExpiredSessions) {
        final Set<String> sessionsUsedByPrincipal = principals.get(principal);

        if (sessionsUsedByPrincipal == null) {
            return Collections.emptyList();
        }

        List<SessionInformation> list = new ArrayList<SessionInformation>(sessionsUsedByPrincipal.size());

        for (String sessionId : sessionsUsedByPrincipal) {
            SessionInformation sessionInformation = getSessionInformation(sessionId);

            if (sessionInformation == null) {
                continue;
            }

            if (includeExpiredSessions || !sessionInformation.isExpired()) {
                list.add(sessionInformation);
            }
        }

        return list;
    }


    // 根据ID获取对应的SessionInformation
    @Override
    public SessionInformation getSessionInformation(String sessionId) {
        Assert.hasText(sessionId, "SessionId required as per interface contract");
        return sessionIds.get(sessionId);
    }

    // 实现onApplicationEvent接口，表明处理SessionDestoryedEvent事件
    @Override
    public void onApplicationEvent(SessionDestroyedEvent event) {
        String sessionId = event.getId();
        //删除对应sessionId相关的数据
        removeSessionInformation(sessionId);
    }
    // 刷新最近操作日期
    @Override
    public void refreshLastRequest(String sessionId) {
        Assert.hasText(sessionId, "SessionId required as per interface contract");

        SessionInformation info = getSessionInformation(sessionId);

        if (info != null) {
            info.refreshLastRequest();
        }
    }

    /**
     * session注册
     * @param sessionId
     * @param principal
     */
    @Override
        public void registerNewSession(String sessionId, Object principal) {
        Assert.hasText(sessionId, "SessionId required as per interface contract");
        Assert.notNull(principal, "Principal required as per interface contract");

        logger.debug("Registering session {} , for principal {}" ,sessionId, principal);

        if (getSessionInformation(sessionId) != null) {
            removeSessionInformation(sessionId);
        }

        sessionIds.put(sessionId, new SessionInformationObject(principal, sessionId, new Date()));
      // 判断用户是否存在， 用Set即可实现去重
        Set<String> sessionsUsedByPrincipal = principals.get(principal);
       // 若用户存在，将当前sessionId添加到对应的集合中
        if (sessionsUsedByPrincipal == null) {
            sessionsUsedByPrincipal = new CopyOnWriteArraySet<String>();
            Set<String> prevSessionsUsedByPrincipal = principals.putIfAbsent(principal,
                    sessionsUsedByPrincipal);
            if (prevSessionsUsedByPrincipal != null) {
                sessionsUsedByPrincipal = prevSessionsUsedByPrincipal;
            }
        }

        sessionsUsedByPrincipal.add(sessionId);

        logger.warn("Sessions used by '{} ' : {}" ,principal, sessionsUsedByPrincipal);
    }

    /**
     * 注销session
     * @param sessionId
     */
    @Override
    public void removeSessionInformation(String sessionId) {
        Assert.hasText(sessionId, "SessionId required as per interface contract");

        SessionInformation info = getSessionInformation(sessionId);

        if (info == null) {
            return;
        }

        logger.debug("Removing session {}  from set of registered sessions",sessionId);

        sessionIds.remove(sessionId);

        Set<String> sessionsUsedByPrincipal = principals.get(info.getPrincipal());

        if (sessionsUsedByPrincipal == null) {
            return;
        }

        logger.debug("Removing session {}  from principal's set of registered sessions",sessionId);

        sessionsUsedByPrincipal.remove(sessionId);

        if (sessionsUsedByPrincipal.isEmpty()) {
            // No need to keep object in principals Map anymore
            logger.debug("Removing principal {} from registry",info.getPrincipal());
            principals.remove(info.getPrincipal());
        }

        logger.warn("Sessions used by '{} ' : {}" ,info.getPrincipal(), sessionsUsedByPrincipal);
    }

}
